package algorithm;

import java.util.ArrayList;
import java.util.List;

import entity.DeviceType;
import entity.FourTuple;
import entity.TwoTuple;
import problem.MLSIAP;
import problem.Problem;
import solution.SIAPSolution;

public class GAForMLSIAP extends GA<Integer, Double, SIAPSolution, FourTuple<Double, Double, Integer, Double>> {

	MLSIAP Mproblem;
	int instanceId;
	int N1, N2, N;
	int M1, M2, M;
	int groupUpper, groupLower;
	int S;
	int L;

	public GAForMLSIAP(Problem<Integer, FourTuple<Double, Double, Integer, Double>, Double> problem, boolean isMax,
			String algName, int popSize, int NFE, double pc, double pm) {
		super(problem, isMax, algName, popSize, NFE, pc, pm);
		Mproblem = (MLSIAP) problem;
		getParameters();
	}

	private void getParameters() {
		instanceId = Mproblem.getInstanceId();
		N1 = Mproblem.getN1();
		N2 = Mproblem.getN2();
		N = Mproblem.getN();
		M1 = Mproblem.getM1();
		M2 = Mproblem.getM2();
		M = Mproblem.getM();
		groupUpper = Mproblem.getUpper();
		groupLower = Mproblem.getLower();
		S = Mproblem.getS();
		L = groupUpper - groupLower;
	}

	@Override
	public void select() {
		/* 用目标函数值的倒数表示适应度，达到适应度越大，选中概率越大的目的 */
		// 轮盘
		double[] wheel = new double[popSize];
		wheel[0] = 1 / population.get(0).getValue();
		for (int i = 1; i < popSize; i++) {
			wheel[i] = wheel[i - 1] + 1 / population.get(i).getValue();
		}

		for (int i = 0; i < popSize; i++) {
			wheel[i] /= wheel[popSize - 1];
		}

		List<SIAPSolution> candidates = new ArrayList<SIAPSolution>();
		// 选出popSize个候选人
		for (int i = 0; i < popSize; i++) {
			double r1 = random.nextDouble();
			for (int j = 0; j < wheel.length; j++) {
				if (r1 < wheel[j]) {
					try {
						candidates.add((SIAPSolution) population.get(j).clone());
						break;
					} catch (CloneNotSupportedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		// 用候选种群代替原来的种群
		population = candidates;
	}

	@Override
	public void breeding() {
		// 保存需要交叉的成员
		List<SIAPSolution> parents = new ArrayList<SIAPSolution>();
		// 保存无需交叉的成员
		List<SIAPSolution> bachelors = new ArrayList<SIAPSolution>();

		for (int i = 0; i < popSize; i++) {
			if (random.nextDouble() < Pc) {
				parents.add(population.get(i));
			} else {
				bachelors.add(population.get(i));
			}
		}

		// 进行交叉
		population.clear();
		for (int i = 0; i < parents.size() / 2; i++) {
			// 交叉点
			int crossPoint = random.nextInt(N);
			try {
				SIAPSolution child1 = (SIAPSolution) parents.get(2 * i).clone();
				SIAPSolution child2 = (SIAPSolution) parents.get(2 * i + 1).clone();
				Integer[] content1 = child1.getContent();
				Integer[] content2 = child2.getContent();
				for (int j = crossPoint; j < N; j++) {
					content1[j] = parents.get(2 * i + 1).getContent()[j];
					content2[j] = parents.get(2 * i).getContent()[j];
				}
				population.add(child1);
				population.add(child2);

			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
		// 如果是奇数，最后一个解和它前面一个解进行交叉
		if (parents.size() % 2 == 1) {
			// 交叉点
			int crossPoint = random.nextInt(N);
			try {
				SIAPSolution parent1 = parents.get(parents.size() - 2);
				SIAPSolution parent2 = parents.get(parents.size() - 1);
				SIAPSolution child1 = (SIAPSolution) parent1.clone();
				SIAPSolution child2 = (SIAPSolution) parent2.clone();
				Integer[] content1 = child1.getContent();
				Integer[] content2 = child2.getContent();
				for (int j = crossPoint; j < N; j++) {
					content1[j] = parent1.getContent()[j];
					content2[j] = parent2.getContent()[j];
				}
				// 只用添加后一个孩子
				population.add(child2);

			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}

		population.addAll(bachelors);

	}

	@Override
	public void mutation() {
		for (int i = 0; i < popSize; i++) {
			SIAPSolution solution = population.get(i);
			Integer[] content = solution.getContent();
			for (int j = 0; j < N; j++) {
				// 如果符合变异概率，重新分组
				if (random.nextDouble() < Pm) {
					content[j] = random.nextInt(groupUpper - groupLower + 1) + groupLower;
				}
			}
			// 重新分组
			assignInspectors(content);
		}
	}

	@Override
	public void initialize() {
		for (int i = 0; i < popSize; i++) {
			int j = 0;
			Integer[] content = new Integer[N + M];
			// 产生乘客分组的解
			for (; j < N; j++) {
				content[j] = random.nextInt(groupUpper - groupLower + 1) + groupLower;
			}
			// 产生设备人员分配的解
			assignInspectors(content);

			SIAPSolution solution = new SIAPSolution(content, groupUpper, groupLower, i, Mproblem.getDimension());
			population.add(solution);
		}
	}

	/**
	 * 根据每个设备th=Th情况下，检查人员检查乘客的总时间，降序排序，从前往后分配检查人员
	 * 
	 * @param content
	 */
	private void assignInspectors(Integer[] content) {
		DeviceType[] devices = Mproblem.calTotolTimeOfEveryDevice(content);
		// 防止空指针异常
		for (int i = N; i < N + M; i++) {
			content[i] = 0;
		}
		int temp = S;
		for (int i = 0; i < devices.length; i++) {
			// 没有可以额外分配的检查人员
			if (temp == 0) {
				break;
			} else if (temp >= devices[i].getNDh()) {
				content[N + devices[i].getH() - 1] = devices[i].getNDh();
				// 把分配的检查人员减掉
				temp -= devices[i].getNDh();
			} else {
				content[N + devices[i].getH() - 1] = temp;
				temp = 0;
			}
		}
	}

	private void evaluateOneSolution(SIAPSolution solution) {
		// 计算适应度和约束值
		FourTuple<Double, Double, Integer, Double> result = Mproblem.calculate(solution);
		solution.setValue(result.getFirst());
		solution.setValueOfPFC(result.getSecond());
		solution.setTotalS(result.getThrid());
		solution.setMaxTime(result.getFourth());
		// 增加函数评估次数
		current_NFE++;
		// 统计
		if (current_NFE % 1000 == 0) {
			TwoTuple<Integer, SIAPSolution> record = new TwoTuple<Integer, SIAPSolution>(current_NFE, best);
			records.add(record);
		}
	}

	@Override
	protected void evaluate() {
		if (best == null) {
			best = population.get(0);
		}

		// 评估每个染色体的适应度
		for (int i = 0; i < popSize; i++) {
			SIAPSolution solution = population.get(i);
			evaluateOneSolution(solution);
			if (solution.getValue() < best.getValue()) {
				best = solution;
			}
		}
	}

	@Override
	public void updateParameters() {

	}

	@Override
	public void evolve() {
		select();
		breeding();
		mutation();
		// 重新评估
		evaluate();

	}

}
